////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   PlatformSoundFmodEx400Event.cpp
//  Version:     v1.00
//  Created:     28/07/2005 by Tomas
//  Compilers:   Visual Studio.NET
//  Description: FmodEx 4.00 Implementation of a platform dependent Sound Event
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"

#ifdef SOUNDSYSTEM_USE_FMODEX400

#include "PlatformSoundFmodEx400Event.h"
#include "SoundBufferFmodEx400.h"
#include "AudioDeviceFmodEx400.h"
#include "Sound.h"
#include "FileCacheManager.h"
#include "SoundCVars.h"
#include <CrySizer.h>
#include "SoundSystem.h"
#include <FmodEx/inc/fmod_errors.h>
#include "ITimer.h"
#include <StlUtils.h>

//////////////////////////////////////////////////////////////////////////
#define IS_FMODERROR (m_ExResult != FMOD_OK )
#define IS_FMODOK    (m_ExResult == FMOD_OK )

FMOD_RESULT F_CALLBACK CPlatformSoundFmodEx400Event::OnCallBack(FMOD_EVENT* event, FMOD_EVENT_CALLBACKTYPE type, void* param1, void* param2, void* userdata)
{
	if (CSound::m_pSSys)
	{
		tSoundID const nSoundID         = reinterpret_cast<tSoundID>(userdata);
		_smart_ptr<CSound> const pSound = static_cast<CSound*>(CSound::m_pSSys->GetSound(nSoundID));

		if (pSound)
		{
			CPlatformSoundFmodEx400Event* const pPlatformSound = static_cast<CPlatformSoundFmodEx400Event* const>(pSound->GetPlatformSoundPtr());

			if (pPlatformSound)
			{
				switch(type) 
				{
				case FMOD_EVENT_CALLBACKTYPE_SOUNDDEF_START:
					{
						if (pPlatformSound->m_State == pssLOADING || pPlatformSound->m_State == pssREADY)
						{
							pPlatformSound->m_State = pssPLAYING;
							pSound->OnEvent(SOUND_EVENT_ON_PLAYBACK_STARTED);
						}

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
						if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_EVENT_SOUNDS)
						{
							CSound::m_pSSys->Log(eSLT_Message, "<Sound> SoundDefStart (SoundID: %d)\n", nSoundID);
						}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

						break;
					}
				case FMOD_EVENT_CALLBACKTYPE_SOUNDDEF_END:
					{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
						if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_EVENT_SOUNDS)
						{
							CSound::m_pSSys->Log(eSLT_Message, "<Sound> SoundDefEnd (SoundID: %d)\n", nSoundID);
						}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

						break;
					}
				case FMOD_EVENT_CALLBACKTYPE_SOUNDDEF_CREATE:
					{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
						if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_EVENT_SOUNDS)
						{
							CSound::m_pSSys->Log(eSLT_Message, "<Sound> SoundDefCreated (SoundID: %d)\n", nSoundID);
						}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

						CSoundBuffer* const pSoundBuffer = pSound->GetSoundBufferPtr();

						if (pSoundBuffer && pSoundBuffer->GetProps()->eBufferType != btEVENT)
						{
							FMOD::Sound* const pTempSound         = static_cast<FMOD::Sound* const>(pSound->GetSoundBufferPtr()->GetAssetHandle());
							*(static_cast<FMOD::Sound**>(param2)) = pTempSound;

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
							if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_PROGRAMMERSOUND)
							{
								CSound::m_pSSys->Log(eSLT_Always, "----> SoundDef %s CREATE +1", pSound->GetSoundBufferPtr()->GetProps()->sName.c_str());
							}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

							if (pSound->SoundDefCreated() == 1)
							{
								// Increase the ref count only on the first sound definition.
								// Make sure to keep it for as long as FMOD works on this sound.
								pSoundBuffer->AddRef();
							}
						}
						else
						{
							return FMOD_ERR_NOTREADY;
						}

						break;
					}
				case FMOD_EVENT_CALLBACKTYPE_SOUNDDEF_RELEASE:
					{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
						if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_EVENT_SOUNDS)
						{
							CSound::m_pSSys->Log(eSLT_Message, "<Sound> SoundDefReleased (SoundID: %d)\n", nSoundID);
						}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

						// Triggering the radio end squelch we need fadeout time for that to work.
						if ((pSound->GetFlags() & FLAG_SOUND_PARAM_SQUELCH) != 0)
						{
							float fOldSquelch = 0.0f;
							pPlatformSound->GetParamByName("squelch", &fOldSquelch, false);
							pPlatformSound->SetParamByName("squelch", 0.0f, false);
							pPlatformSound->SetParamByName("squelch", fOldSquelch, false);
						}

						CSoundBuffer* const pSoundBuffer = pSound->GetSoundBufferPtr();

						if (pSoundBuffer && pSoundBuffer->GetProps()->eBufferType != btEVENT)
						{
							if (pSoundBuffer->GetAssetHandle() == param2) // checking that FMOD_EVENT_CALLBACKTYPE_SOUNDDEF_CREATE called with this asset handle
							{
								if (pSound->SoundDefReleased() == 0)
								{
									// Only release our ref count once the last sound definition has been freed.
									pSound->SetState(eSoundState_ProgrammerSoundReleased);
									pSoundBuffer->Release();
								}

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
								if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_PROGRAMMERSOUND)
								{
									CSound::m_pSSys->Log(eSLT_Always, "----> SoundDef %s RELEASE -1", pSound->GetSoundBufferPtr()->GetProps()->sName.c_str());
								}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE
							}
							else
							{
								// This should actually never happen!
								assert(0);
							}
						}

						break;
					}
				case FMOD_EVENT_CALLBACKTYPE_SOUNDDEF_INFO:
					{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
						char const* const sSoundDefName = static_cast<char const* const>(param1);

						if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_EVENT_SOUNDS)
						{
							CSound::m_pSSys->Log(eSLT_Message, "<Sound> SoundDefInfo (Name: %s) (SoundID: %d)\n", sSoundDefName, nSoundID);
						}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

						pSound->OnEvent(SOUND_EVENT_ON_INFO);

						break;
					}
				case FMOD_EVENT_CALLBACKTYPE_SYNCPOINT:
					{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
						switch (CSound::m_pSSys->g_nDebugSound)
						{
						case SOUNDSYSTEM_DEBUG_EVENT_SOUNDS:
							{
								CSound::m_pSSys->Log(eSLT_Message, "<Sound> SyncPoint (SoundID: %d)\n", nSoundID);

								break;
							}
						case SOUNDSYSTEM_DEBUG_SYNCPOINTS:
							{
								int nPosInMS = pSound->GetCurrentSamplePos(true);
								CSound::m_pSSys->Log(eSLT_Message, "<Sound> --> Syncpoint arrived: %s, Time:%.2f, PosInMS:%d", pSound->GetName(), gEnv->pTimer->GetAsyncCurTime(), nPosInMS);

								break;
							}
						}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

						pSound->OnEvent(SOUND_EVENT_ON_SYNCHPOINT);

						if (pPlatformSound->m_State == pssWAITINGFORSYNC)
						{
							// Try to pause this event as quickly as possible.
							FMOD::Event* const pEvent = static_cast<FMOD::Event* const>(pPlatformSound->GetSoundHandle());

							if (pEvent)
							{
								FMOD_RESULT ExResult = pEvent->setPaused(true);
							}

							// Do it again through the platform sound instance for integrity reasons.
							bool bPause = true;
							ptParamBOOL TempParam(bPause);
							pPlatformSound->SetParamByType(pspPAUSEMODE, &TempParam);
							pPlatformSound->m_State = pssSYNCARRIVED;

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
							if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_SYNCPOINTS)
							{
								CSound::m_pSSys->Log(eSLT_Message, "<Sound> --> Waited for Syncpoint: %s, Time:%.2f", pSound->GetName(), gEnv->pTimer->GetAsyncCurTime());
							}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE
						}

						break;
					}
				case FMOD_EVENT_CALLBACKTYPE_NET_MODIFIED:
					{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
						if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_EVENT_SOUNDS)
						{
							CSound::m_pSSys->Log(eSLT_Message, "<Sound> NetModified (SoundID: %d)\n", nSoundID);
						}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

						// Reset the max volume that is used to normalize the sound's volume.
						FMOD::Event* const pEvent = static_cast<FMOD::Event* const>(pPlatformSound->GetSoundHandle());

						if (pEvent)
						{
							pEvent->getVolume(&(pPlatformSound->m_fMaxVolume));
						}

						break;
					}
				case FMOD_EVENT_CALLBACKTYPE_EVENTSTARTED:
					{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
						if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_EVENT_SOUNDS)
						{
							CSound::m_pSSys->Log(eSLT_Message, "<Sound> EventStarted (SoundID: %d)\n", nSoundID);
						}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

						pPlatformSound->UpdateStreamCount(true);

						break;
					}
				case FMOD_EVENT_CALLBACKTYPE_EVENTFINISHED:
					{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
						switch (CSound::m_pSSys->g_nDebugSound)
						{
						case SOUNDSYSTEM_DEBUG_EVENT_SOUNDS:
							{
								CSound::m_pSSys->Log(eSLT_Message, "<Sound> EventFinished (SoundID: %d)\n", nSoundID);

								break;
							}
						case SOUNDSYSTEM_DEBUG_PROGRAMMERSOUND:
							{
								if ((pSound->GetFlags() & FLAG_SOUND_VOICE) != 0)
								{
									CSound::m_pSSys->Log(eSLT_Always, "----> SoundDef %s EVENT FINISHED", pSound->GetSoundBufferPtr()->GetProps()->sName.c_str());
								}

								break;
							}
						}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

						pPlatformSound->m_State = pssSTOPPED;
						pPlatformSound->UpdateStreamCount(false);

						break;
					}
				case FMOD_EVENT_CALLBACKTYPE_STOLEN:
					{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
						if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_EVENT_SOUNDS)
						{
							CSound::m_pSSys->Log(eSLT_Message, "<Sound> EventStolen (SoundID: %d)\n", nSoundID);
						}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

						pPlatformSound->m_State = pssSTOLEN;
						pPlatformSound->FreeSoundHandle();
						pPlatformSound->InvalidateParameterCache();

						break;
					}
				default:
					{
						break;
					}
				}
			}
		}
	}

	return FMOD_OK;
}


//////////////////////////////////////////////////////////////////////////
CPlatformSoundFmodEx400Event::CPlatformSoundFmodEx400Event(ISound*	pSound, FMOD::System* pCSEX, const char *sEventName)
:	m_pSound((CSound*)pSound),
	m_pCSEX(pCSEX),
	m_pAudioDevice(NULL),
	m_fDistance(0.0f),
	m_fLoudness(0.0f),
	m_fVolume(0.0f),
	m_pEvent(NULL),
	m_nEventIndex(-1),
	m_nRefCount(0),
	m_ExResult(FMOD_OK),
	m_fMaxRadius(0.0f),
	m_fMaxVolume(-1.0f),
	m_sEventName(sEventName),
	m_State(pssNONE),
	m_nLenghtInMs(0),
	m_nMaxSpawns(0),
	m_nDistanceIndex(-1),
	m_nDistanceParamIndex(-1),
	m_nCurrentSpawns(0),
	m_bParameterCacheValid(false),
	m_bAFCMCached(true),
	m_bPreloadedToFMOD(true)
{
	assert(m_pCSEX);

	if (pSound)
	{
		m_pAudioDevice = (CAudioDeviceFmodEx400*)m_pSound->GetSoundSystemPtr()->GetIAudioDevice();
		Reset(pSound, sEventName);
	}
}

//////////////////////////////////////////////////////////////////////////
CPlatformSoundFmodEx400Event::~CPlatformSoundFmodEx400Event(void)
{
	FreeMemory();
	FreeSoundHandle();		
}

////////////////////////////////////////////////////////////////////////////
void CPlatformSoundFmodEx400Event::Reset(ISound*	pSound, const char *sEventName)
{	
	assert(pSound);
	m_pSound					= (CSound*)pSound;
	m_pEvent					= NULL;
	m_nEventIndex			= -1;
	m_nRefCount				= 0;
	m_ExResult				= FMOD_OK;
	m_fMaxRadius			= 0.0f;
	m_fMaxVolume			= -1.0f;
	m_sEventName			= sEventName;
	m_State						= pssNONE;
	m_nLenghtInMs			= 0;
	m_nMaxSpawns			= 0;
	m_nDistanceIndex	= -1;
	m_nDistanceParamIndex = -1;
	m_nCurrentSpawns	= 0;
	m_QueuedParameter.clear();
	m_bParameterCacheValid = false;
	m_bAFCMCached      = true;
	m_bPreloadedToFMOD = true;

	m_fDistance = 0.0f;
	m_bHasLoudness = false;
	m_fLoudness = 0.0f;
	m_fVolume = 0.0f;

	if (!m_pAudioDevice)
		m_pAudioDevice = (CAudioDeviceFmodEx400*)m_pSound->GetSoundSystemPtr()->GetIAudioDevice();
	
	// Prevent some runtime allocations by reserving space for at least 36 parameters
	size_t const nNum = max((size_t)36, CSound::m_pSSys->m_ParameterNames.size());
	m_ParameterIndex.resize(nNum);

	// Make sure to invalid all entries
	for (size_t i = 0; i < nNum; ++i)
		m_ParameterIndex[i].nEventIndex = -1;
}

//////////////////////////////////////////////////////////////////////////
void CPlatformSoundFmodEx400Event::FreeMemory()
{
	stl::free_container(m_ParameterIndex);
	stl::free_container(m_QueuedParameter);
	stl::free_container(m_sEventName);
}

//////////////////////////////////////////////////////////////////////////
int32 CPlatformSoundFmodEx400Event::AddRef()
{
	return (++m_nRefCount);
}

//////////////////////////////////////////////////////////////////////////
int32 CPlatformSoundFmodEx400Event::Release()
{
	--m_nRefCount;
	if (m_nRefCount <= 0)
	{
		m_pAudioDevice->RemovePlatformSound(this);
		return (0);
	}
	
	return m_nRefCount;
}

////////////////////////////////////////////////////////////////////////////
void CPlatformSoundFmodEx400Event::FmodErrorOutput(const char * sDescription, ESoundLogType LogType)
{
	if (m_ExResult == FMOD_ERR_INVALID_HANDLE || m_ExResult == FMOD_ERR_EVENT_MISMATCH)
	{
		if (m_ExResult == FMOD_ERR_INVALID_HANDLE && CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_DETAIL)
		{
			TFixedResourceName sTemp = "Invalid Handle - Playback maximum reached on ";
			sTemp += m_pSound->GetName();
			sTemp += "?";

			gEnv->pSoundSystem->Log(eSLT_Warning, "<Sound> PSE: %s \n", sTemp.c_str());
		}

		// Reset these only in the event of a real error!
		m_pEvent = NULL;
		m_State  = pssERROR;
		m_pSound->Update();
	}

	gEnv->pSoundSystem->Log(LogType, "<Sound> PSE: %s %s\n", m_pSound->GetName(), sDescription, m_ExResult);
	gEnv->pSoundSystem->Log(LogType, "<Sound> PSE: (%d) %s\n", m_ExResult, FMOD_ErrorString(m_ExResult));
}

//////////////////////////////////////////////////////////////////////////
tSoundHandle CPlatformSoundFmodEx400Event::GetSoundHandle() const
{ 
	//if (m_pExChannel == NULL) 
		//return NULL;
	return ((tSoundHandle)m_pEvent); 
}

//////////////////////////////////////////////////////////////////////////
bool CPlatformSoundFmodEx400Event::Set3DPosition(Vec3* pvPosition, Vec3* pvVelocity, Vec3* pvOrientation)
{
	FMOD_VECTOR vExPos;
	vExPos.x = vExPos.y = vExPos.z = 0.0f;
	FMOD_VECTOR vExVel;
	vExVel.x = vExVel.y = vExVel.z = 0.0f;
	FMOD_VECTOR vExOrient;
	vExOrient.x = vExOrient.y = vExOrient.z = 0.0f;

	CSoundSystem* pSys = m_pSound->GetSoundSystemPtr();
	if (!pSys) return (false);

	//SListener *pListener = NULL;
	// axis switch
	if (pvPosition)
	{
		vExPos.x = pvPosition->x;
		vExPos.y = pvPosition->z;
		vExPos.z = pvPosition->y;
	}

	if (pvVelocity)
	{
		CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );

		// Doppler Value here does not have to be multiplied with the DopplerScale of the event
		// because FMOD will do that internally anyway
		
		vExVel.x = clamp(pvVelocity->x, -200.0f, 200.0f);
		vExVel.y = clamp(pvVelocity->z, -200.0f, 200.0f);
		vExVel.z = clamp(pvVelocity->y, -200.0f, 200.0f);
	}

	if (pvOrientation)
	{
		vExOrient.x = pvOrientation->x;
		vExOrient.y = pvOrientation->z;
		vExOrient.z = pvOrientation->y;
	}

	if (m_pEvent)
	{
		{		
			if (pSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
				m_pAudioDevice->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENT_SET3DATTRIBUTES, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)m_pEvent), ptParamVEC3F32(Vec3(vExPos.x, vExPos.y, vExPos.z)), ptParamVEC3F32(Vec3(vExVel.x, vExVel.y, vExVel.z)));

			CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );
			m_ExResult = m_pEvent->set3DAttributes((pvPosition ? &vExPos : NULL), (pvVelocity ? &vExVel : NULL), (pvOrientation ? &vExOrient : NULL));
		}
		if (IS_FMODERROR)
		{
			FmodErrorOutput("set 3d event position failed! ", eSLT_Message);
			return false;
		}
	}
	return (true);
}

//////////////////////////////////////////////////////////////////////////
void CPlatformSoundFmodEx400Event::SetObstruction(const SObstruction *pObstruction)
{
	assert (pObstruction);

	if (m_pEvent) // && m_State != pssINFOONLY)
	{
		float fDirect = pObstruction->GetDirect();
		float fReverb = pObstruction->GetReverb();
		
		assert(fDirect <= 1.0f && fDirect >= 0.0f);
		assert(fReverb <= 1.0f && fReverb >= 0.0f);

		{
			CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );
			m_ExResult = m_pEvent->set3DOcclusion(fDirect, fReverb);
		}
		if (IS_FMODERROR)
		{
			//FmodErrorOutput("set set3DOcclusion failed! ", eSLT_Message);
			return;
		}
	}
}

// Gets and Sets Parameter defined in the enumSoundParam list
//////////////////////////////////////////////////////////////////////////
bool CPlatformSoundFmodEx400Event::GetParamByType(enumPlatformSoundParamSemantics eSemantics, ptParam* pParam)
{
	CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_GET_PARAM_BY_TYPE) );

	bool bSuccess = false;

	// Stuff we can do with and without a valid m_pEvent pointer.
	bSuccess = (eSemantics == pspEVENTNAME) ? pParam->SetValue(m_sEventName) : false;

	// Stuff for which we need a valid m_pEvent pointer.
	if (m_pEvent) 
	{
		switch (eSemantics)
		{
		case pspNONE:
			{
				break;
			}
		case pspSOUNDTYPE:
			{
				break;
			}
		case pspSAMPLETYPE:
			{
				break;
			}
		case pspFREQUENCY:
			{
				int32 nTemp;
				if (pParam->GetValue(nTemp))
				{
					pParam->SetValue(nTemp);
					bSuccess = true;
				}

				break;
			}
		case pspVOLUME:
			{
				float fTemp = 0.0f;

				if (pParam->GetValue(fTemp))
				{
					m_ExResult = m_pEvent->getVolume(&fTemp);
					if (IS_FMODOK)
					{
						// volume needs to be normalized with allowed MaxVolume (which is -1.0 the first time)
						fTemp *= abs(m_fMaxVolume);
						pParam->SetValue(fTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <volume> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspLOUDNESSINDB:
			{
				float fTemp = 60.0f;

				if (pParam->GetValue(fTemp))
				{
					m_ExResult = m_pEvent->getProperty("loudness", &fTemp);
					if (IS_FMODOK)
					{
						pParam->SetValue(fTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <loudness> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspIS3D:
			{
				bool bTemp = false;

				if (pParam->GetValue(bTemp))
				{
					FMOD_MODE Mode;
					m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_MODE, &Mode);

					if (IS_FMODOK)
					{
						if (Mode & FMOD_3D)
							bTemp = true;

						pParam->SetValue(bTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <is3D/Mode> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspISPLAYING:
			{
				bool bTemp = false;

				if (pParam->GetValue(bTemp))
				{
					FMOD_EVENT_STATE EventState;
					m_ExResult = m_pEvent->getState(&EventState);

					if (IS_FMODOK)
					{
						if (EventState & FMOD_EVENT_STATE_PLAYING || EventState & FMOD_EVENT_STATE_LOADING ) // EVENT_STATE_CHANNELSACTIVE
							bTemp = true;
						else
							bTemp = false;

						pParam->SetValue(bTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event state failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspISSIMPLE:
			{
				bool bTemp = false;

				if (pParam->GetValue(bTemp))
				{
					int nType;
					m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_EVENTTYPE, &nType);

					if (IS_FMODOK)
					{
						if (nType == 0)
							bTemp = true;

						pParam->SetValue(bTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <IsSimple/EventType> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspISSTREAM:
			{
				bool bTemp = false;
				
				if (pParam->GetValue(bTemp))
				{
					FMOD_EVENT_INFO Info;
					FMOD_EVENT_WAVEBANKINFO BankInfo = {{0},0};
					Info.maxwavebanks = 1;
					Info.wavebankinfo = &BankInfo;

					m_ExResult = m_pEvent->getInfo(0, 0, &Info);

					if (IS_FMODOK)
					{
						// This test only evaluates the first wavebank
						// Type 0 indicates a streamed wavebank
						pParam->SetValue(BankInfo.type == 0);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event info <is stream> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspLOOPMODE:
			{
				bool bTemp = false;

				if (pParam->GetValue(bTemp))
				{
					int nOneShot = 0;
					m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_ONESHOT, &nOneShot);

					if (IS_FMODOK)
					{
						pParam->SetValue(nOneShot == 0);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <oneshot> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspPAUSEMODE:
			{
				bool bTemp = false;

				if (pParam->GetValue(bTemp))
				{
					m_ExResult = m_pEvent->getPaused(&bTemp);

					if (IS_FMODOK)
					{
						pParam->SetValue(bTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event <paused> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspSPEAKERPAN:
			{
				break;
			}
		case pspSPEAKER3DPAN:
			{
				break;
			}
		case pspSAMPLEPOSITION: // sample position is not supported by sound events
			{
				break;
			}
		case pspTIMEPOSITION:
			{
				int32 nTemp = 0;

				if (pParam->GetValue(nTemp))
				{
					FMOD_EVENT_INFO Info;

					m_ExResult = m_pEvent->getInfo(0, 0, &Info);

					if (IS_FMODOK)
					{
						pParam->SetValue(Info.positionms);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event info <time pos> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case psp3DPOSITION:
			{
				Vec3 VecTemp;

				if (pParam->GetValue(VecTemp))
				{
					FMOD_VECTOR pos;
					m_ExResult = m_pEvent->get3DAttributes(&pos, 0);

					if (IS_FMODOK)
					{
						VecTemp.x = pos.x;
						VecTemp.y = pos.z;
						VecTemp.z = pos.y;
						pParam->SetValue(VecTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <3d position> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case psp3DVELOCITY:
			{
				Vec3 VecTemp;

				if (pParam->GetValue(VecTemp))
				{
					FMOD_VECTOR vel;
					m_ExResult = m_pEvent->get3DAttributes(0, &vel);

					if (IS_FMODOK)
					{
						VecTemp.x = vel.x;
						VecTemp.y = vel.z;
						VecTemp.z = vel.y;
						pParam->SetValue(VecTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <3d velocity> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case psp3DDOPPLERSCALE:
			{
				float fTemp = 0.0f;

				if (pParam->GetValue(fTemp))
				{
					m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_3D_DOPPLERSCALE, &fTemp);
					
					if (IS_FMODOK)
					{
						pParam->SetValue(fTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <doppler scale> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspPRIORITY:
			{
				int32 nTemp = 0;

				if (pParam->GetValue(nTemp))
				{
					m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_PRIORITY, &nTemp);
					
					if (IS_FMODOK)
					{
						pParam->SetValue(nTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <priority> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspLENGTHINMS:
			{
				int32 nTemp = 0;

				if (pParam->GetValue(nTemp))
				{
					FMOD_EVENT_INFO Info;
					m_ExResult = m_pEvent->getInfo(0, 0, &Info);

					if (IS_FMODOK)
					{
						m_nLenghtInMs = Info.lengthms;

						// A length of -1 indicates a looped event
						// TODO: Double check whether to consider a length of 0 to be an error or not
						if (m_nLenghtInMs >= 0)
						{
							pParam->SetValue(m_nLenghtInMs);
							bSuccess = true;
						}
					}
					else
					{
						FmodErrorOutput("get event info <length ms> failed! ", eSLT_Message);
					}
				}
				
				break;
			}
		case pspLENGTHINBYTES:
			{
				int32 nTemp = 0;

				if (pParam->GetValue(nTemp))
				{
					unsigned int nMemoryUsed = 0;

					m_ExResult = m_pEvent->getMemoryInfo(FMOD_MEMBITS_SOUND, 0, &nMemoryUsed, 0);

					if (IS_FMODOK)
					{
						if (nMemoryUsed > 0)
						{
							nTemp = (int32)nMemoryUsed;
							pParam->SetValue(nTemp);
							bSuccess = true;
						}
					}
					else
					{
						FmodErrorOutput("getMemoryInfo on event <SOUND> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspFXEFFECT:
			{
				break;
			}
		case pspMAXRADIUS:
			{
				float fMax = 1.0f;

				if (pParam->GetValue(fMax))
				{
					float fMin = 0.0f;

					// first test if there is a <distance> parameter
					FMOD::EventParameter *pParameter = NULL;
					m_ExResult = m_pEvent->getParameter("distance", &pParameter);

					if (IS_FMODOK)
					{
						// if there is one, then get the values through the properties because if INFOONLY
						//m_ExResult = m_pEvent->getPropertyByIndex(FMOD::EVENTPROPERTY_3D_MAXDISTANCE, &fMax);
						//if (IS_FMODERROR)
						//{
						//	FmodErrorOutput("get 3D maxdistance property failed! ", eSLT_Message);
						//	return false;
						//}

						m_ExResult = pParameter->getRange(&fMin, &fMax);

						if (IS_FMODOK)
						{
							pParam->SetValue(fMax);
							bSuccess = true;
						}
						else
						{
							FmodErrorOutput("get event parameter <distance> range failed! ", eSLT_Message);
						}
					}
					else
					{
						//FmodErrorOutput("get event parameter <distance> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspAMPLITUDE:
			{
				float fTemp = 0.0f;

				if (pParam->GetValue(fTemp))
				{
					m_ExResult = m_pEvent->getVolume(&fTemp);

					if (IS_FMODOK)
					{
						pParam->SetValue(fTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <amplitude> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspCONEINNERANGLE:
			{
				float fTemp = 0.0f;

				if (pParam->GetValue(fTemp))
				{
					m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_3D_CONEINSIDEANGLE, &fTemp);

					if (IS_FMODOK)
					{
						pParam->SetValue(fTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <cone inside angle> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspCONEOUTERANGLE:
			{
				float fTemp = 0.0f;

				if (pParam->GetValue(fTemp))
				{
					m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_3D_CONEOUTSIDEANGLE, &fTemp);
					
					if (IS_FMODOK)
					{
						pParam->SetValue(fTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <cone outside angle> failed! ", eSLT_Message);
					}
				}

				break;
			}			
		case pspREVERBWET:
			{
				float fTemp = 0.0f;

				if (pParam->GetValue(fTemp))
				{
					m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_REVERBWETLEVEL, &fTemp);

					if (IS_FMODOK)
					{
						pParam->SetValue(fTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <reverb wet> failed! ", eSLT_Message);
					}
				}

				break;
			}		
		case pspREVERBDRY:
			{
				float fTemp = 0.0f;

				if (pParam->GetValue(fTemp))
				{
					m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_REVERBDRYLEVEL, &fTemp);
					
					if (IS_FMODOK)
					{
						pParam->SetValue(fTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <reverb dry> failed! ", eSLT_Message);
					}
				}

				break;
			}		
		case pspSPREAD:
			{
				float fTemp = 0.0f;

				if (pParam->GetValue(fTemp))
				{
					// first test if there is a <spread> parameter
					FMOD::EventParameter *pParameter = 0;
					m_ExResult = m_pEvent->getParameter("spread", &pParameter);

					if (IS_FMODOK)
					{
						// TODO: make sure this test is really necessary
						// A return value of FMOD_OK should already return a valid pParameter instance
						bSuccess = (pParameter != NULL);
					}
					else
					{
						//FmodErrorOutput("get event parameter <spread> failed! ", eSLT_Message);
					}
				}
				
				break;
			}	
		case pspFADEOUT:
			{
				int nTemp = 0;

				if (pParam->GetValue(nTemp))
				{
					m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_FADEOUT, &nTemp);

					if (IS_FMODOK)
					{
						pParam->SetValue(nTemp);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event property <fadeout time> failed! ", eSLT_Message);
					}
				}

				break;
			}	
		case pspSPAWNEDINSTANCES:
			{
				int32 nTemp = 0;

				if (pParam->GetValue(nTemp))
				{
					FMOD_EVENT_INFO Info;
					m_ExResult = m_pEvent->getInfo(0, 0, &Info);

					if (IS_FMODOK)
					{
						pParam->SetValue(Info.instancesactive);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event info <spawned instances> failed! ", eSLT_Message);
					}
				}

				break;
			}
		case pspCHANNELSUSED:
			{
				int32 nTemp = 0;

				if (pParam->GetValue(nTemp))
				{
					FMOD_EVENT_INFO Info;
					m_ExResult = m_pEvent->getInfo(0, 0, &Info);

					if (IS_FMODOK)
					{
						pParam->SetValue(Info.channelsplaying);
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("get event info <channels used> failed! ", eSLT_Message);
					}
				}

				break;
			}
		}
	}
	
	return bSuccess;
}


//////////////////////////////////////////////////////////////////////////
bool CPlatformSoundFmodEx400Event::SetParamByType(enumPlatformSoundParamSemantics const eSemantics, ptParam const* const pParam, bool const bDisableAutomation /* = false */)
{
	CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_SET_PARAM_BY_TYPE) );

	if (!m_pEvent || m_State == pssWAITINGTOFADEOUT) 
	{
		SQueuedParameter QParam;
		QParam.eSemantic = eSemantics;

		int32 nTemp = 0;

		if (pParam->GetValue(nTemp)) 
			QParam.nValue = nTemp;

		float fTemp = 0.0f;

		if (pParam->GetValue(fTemp)) 
			QParam.fValue = fTemp;

		bool bAlreadyFound = false;
		tvecQueuedParameterIter It2End = m_QueuedParameter.end();
		for (tvecQueuedParameterIter It2 = m_QueuedParameter.begin(); It2!=It2End; ++It2)
		{
			if ((*It2).eSemantic == QParam.eSemantic)
			{
				(*It2).fValue = QParam.fValue;
				(*It2).nValue = QParam.nValue;
				bAlreadyFound = true;
				break;
			}
		}

		if (!bAlreadyFound)
			m_QueuedParameter.push_back(QParam);

		return true;
	}

	FMOD::EventParameter* pParameter = NULL;
	bool bSuccess = false;
	float fTemp   = 0.0f;
	int32 nTemp   = 0;

	switch (eSemantics)
	{
	case pspNONE:
		{
			break;
		}
	case pspSOUNDTYPE:
		{
			break;
		}
	case pspSAMPLETYPE:
		{
			break;
		}
	case pspDISTANCE:
		{
			if (m_nDistanceIndex != -1)
			{
				if (pParam->GetValue(fTemp))
				{
					//if (m_pSound->GetSoundSystemPtr()->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
					//m_pAudioDevice->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENT_SETDISTANCE, gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), (void*) m_pEvent, (void*) &fTemp);

					// Clamp the parameter to the required range.
					fTemp = clamp_tpl(fTemp, m_ParameterIndex[m_nDistanceParamIndex].fRangeMin, m_ParameterIndex[m_nDistanceParamIndex].fRangeMax);

					// prevent updating FMOD if distance change is below 1cm
					if (!m_bParameterCacheValid || fabs(fTemp - m_fDistance) > 0.01f)
					{
						m_ExResult = m_pEvent->getParameterByIndex(m_nDistanceIndex, &pParameter); 

						if (IS_FMODOK)
						{
							if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_DETAIL)
							{
								float fMin, fMax;
								pParameter->getRange(&fMin, &fMax);
								char* sParameterName = 0;
								pParameter->getInfo(0, &sParameterName);
								CSound::m_pSSys->Log(eSLT_Always, "Event: %d Parameter %s value %1.2f out of Range %1.2f - %1.2f", m_pEvent, sParameterName, fTemp, fMin, fMax);
							}

							m_ExResult = pParameter->setValue(fTemp);
							m_fDistance = fTemp;

							if (IS_FMODOK)
							{
								bSuccess = true;
							}
							else
							{
								FmodErrorOutput("set event parameter value <distance> failed! ", eSLT_Message);
							}
						}
						else
						{
							FmodErrorOutput("get event parameter <distance> failed! ", eSLT_Message);
						}
					}
				}
			}

			break;
		}
	case pspFREQUENCY:
		{
			break;
		}
	case pspVOLUME:
		{
			// Do not set the volume on infoonly events, otherwise sound will fade to 0 on reusing the event
			if (m_State != pssINFOONLY)
			{
				if (pParam->GetValue(fTemp))
				{
					fTemp *= abs(m_fMaxVolume);

					if (m_pSound->GetSoundSystemPtr()->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
						m_pAudioDevice->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENT_SETVOLUME, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)m_pEvent), ptParamF32(fTemp));

					if (!m_bParameterCacheValid || (fTemp == 0.0f && m_fVolume != fTemp) || fabs(m_fVolume - fTemp) > 0.01f)
					{
						m_ExResult = m_pEvent->setVolume(fTemp);

						if (IS_FMODOK)
						{
							m_fVolume = fTemp;
							bSuccess  = true;
						}
						else
						{
							FmodErrorOutput("set event property <volume> failed! ", eSLT_Message);
						}
					}
				}
			}

			break;
		}
	case pspISPLAYING: // Don't support that
		{
			break;
		}
	case pspLOOPMODE:
		{
			//bool bTemp = false;
			//FMOD_MODE Mode = FMOD_LOOP_OFF;
			//if (!(pParam->GetValue(bTemp))) 

			//if (bTemp) 
			//	Mode = FMOD_LOOP_NORMAL;
			//bool bPaused;
			//m_pExChannel->getPaused(&bPaused);
			//if (bPaused)
			//	m_pExChannel->setMode(Mode);
			//else
			//{
			//	m_pExChannel->setPaused(true);
			//	m_pExChannel->setMode(Mode);
			//	m_pExChannel->setPaused(false);
			//}
			break;
		}
	case pspPAUSEMODE:
		{
			// Do not pause on infoonly events
			if (m_State != pssINFOONLY)
			{
				bool bTemp = false;

				if (pParam->GetValue(bTemp))
				{
					if (m_pSound->GetSoundSystemPtr()->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
						m_pAudioDevice->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENT_SETPAUSED, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)m_pEvent), ptParamBOOL(bTemp));

					m_ExResult = m_pEvent->setPaused(bTemp);

					if (IS_FMODOK)
					{
						if (bTemp)
							m_State = pssPAUSED;

						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("set event property <paused> failed! ", eSLT_Message);
					}
				}
			}
				
			break;
		}
	case pspSPEAKERPAN:
		{
			break;
		}
	case pspSPEAKER3DPAN:
		{
			break;
		}
	case pspSAMPLEPOSITION: // Sample position is not supported by sound events!
		{
			break;
		}
	case pspTIMEPOSITION:
		{
			// do not set time offset on infoonly events
			if (m_State != pssINFOONLY)
			{
				if (pParam->GetValue(nTemp))
				{
					float fOffsetInSec = nTemp / 1000.0f;

					m_ExResult = m_pEvent->setPropertyByIndex(FMOD_EVENTPROPERTY_TIMEOFFSET, &fOffsetInSec);

					if (IS_FMODOK)
					{
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("set event property <timeoffset> failed! ", eSLT_Message);
					}
				}
			}

			break;
		}		
	case psp3DPOSITION:
		{
			Vec3 VecTemp;

			if (pParam->GetValue(VecTemp))
			{
				// axis swap!
				FMOD_VECTOR pos = {VecTemp.x,VecTemp.z,VecTemp.y};
				m_ExResult = m_pEvent->set3DAttributes(&pos, NULL);

				if (IS_FMODOK)
				{
					bSuccess = true;
				}
				else
				{
					FmodErrorOutput("set event property <3d position> failed! ", eSLT_Message);
				}
			}

			break;
		}
	case psp3DVELOCITY:
		{
			Vec3 VecTemp;

			if (pParam->GetValue(VecTemp))
			{
				// axis swap!
				FMOD_VECTOR vel = {VecTemp.x,VecTemp.z,VecTemp.y};
				m_ExResult = m_pEvent->set3DAttributes(NULL, &vel);

				if (IS_FMODOK)
				{
					bSuccess = true;
				}
				else
				{
					FmodErrorOutput("set event property <3d velocity> failed! ", eSLT_Message);
				}
			}

			break;
		}
	case pspPRIORITY:
		{
			break;
		}
	case pspFXEFFECT:
		{
			break;
		}
	case pspAMPLITUDE:
		{
			break;
		}
	case pspREVERBWET:
		{
			// Do not set the reverb on infoonly events
			if (m_State != pssINFOONLY)
			{
				if (pParam->GetValue(fTemp))
				{
					m_ExResult = m_pEvent->setPropertyByIndex(FMOD_EVENTPROPERTY_REVERBWETLEVEL, &fTemp);

					if (IS_FMODOK)
					{
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("set event property <reverb wet> failed! ", eSLT_Message);
					}
				}
			}

			break;
		}		
	case pspREVERBDRY:
		{
			// Do not set the reverb on infoonly events
			if (m_State != pssINFOONLY)
			{
				if (pParam->GetValue(fTemp))
				{
					m_ExResult = m_pEvent->setPropertyByIndex(FMOD_EVENTPROPERTY_REVERBDRYLEVEL, &fTemp);

					if (IS_FMODOK)
					{
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("set event property <reverb dry> failed! ", eSLT_Message);
					}
				}
			}

			break;
		}		
	case pspSPREAD:
		{
			if (pParam->GetValue(fTemp))
			{
				// TODO: Optimize this through retrieving the parameter by index!
				m_ExResult = m_pEvent->getParameter("spread", &pParameter);

				if (IS_FMODOK)
				{
					m_ExResult = pParameter->setValue(fTemp);

					if (IS_FMODOK)
					{
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("<Sound>: Setting of event parameter <spread> failed!", eSLT_Message);
					}
				}
			}

			break;
		}
	case pspFADEOUT:
		{
			// Do not set the fade out on infoonly events.
			if (m_State != pssINFOONLY)
			{
				if (pParam->GetValue(nTemp))
				{
					m_ExResult = m_pEvent->setPropertyByIndex(FMOD_EVENTPROPERTY_FADEOUT, &nTemp);

					if (IS_FMODOK)
					{
						bSuccess = true;
					}
					else
					{
						FmodErrorOutput("set event property <fadeout time> failed! ", eSLT_Message);
					}
				}
			}
			
			break;
		}	
	case pspSTEALBEHAVIOUR:
		{
			if (pParam->GetValue(nTemp))
			{
				m_ExResult = m_pEvent->setPropertyByIndex(FMOD_EVENTPROPERTY_MAX_PLAYBACKS_BEHAVIOR, &nTemp);

				if (IS_FMODOK)
				{
					bSuccess = true;
				}
				else
				{
					FmodErrorOutput("set event property <steal behaviour> failed! ", eSLT_Message);
				}
			}

			break;
		}
	case pspAFFECTSREVERB:
		{
			if (pParam->GetValue(nTemp))
			{
				m_ExResult = m_pEvent->setPropertyByIndex(FMOD_EVENTPROPERTY_EFFECTS_AFFECT_REVERB, &nTemp);

				if (IS_FMODOK)
				{
					bSuccess = true;
				}
				else
				{
					FmodErrorOutput("set event property <effects affect reverb> failed! ", eSLT_Message);
				}
			}

			break;
		}
	case pspLISTENERANGLE:
		{
			if (pParam->GetValue(fTemp))
			{
				m_ExResult = m_pEvent->getParameter("(listener angle)", &pParameter);

				if (IS_FMODOK)
				{
					m_ExResult = pParameter->disableAutomation(bDisableAutomation);

					if (IS_FMODOK)
					{
						m_ExResult = pParameter->setValue(fTemp);
						bSuccess   = IS_FMODOK;
					}
				}
			}

			if (!bSuccess)
			{
				FmodErrorOutput("set event parameter <(listener angle)> failed! ", eSLT_Message);
			}

			break;
		}
	}

	return bSuccess;
}


// Gets and Sets Parameter defined by string and float value
////////////////////////////////////////////////////////////////////////////
int CPlatformSoundFmodEx400Event::GetParamByName(const char *sParameter, float *fValue, bool bOutputWarning)
{
	int nIndex = -1;

	if (m_pEvent && sParameter) 
	{
		FMOD::EventParameter *pParameter = 0;
		
		{
			CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );
			m_ExResult = m_pEvent->getParameter((char*)sParameter, &pParameter);
		}
		
		if (IS_FMODERROR)
		{
			if (bOutputWarning)
			{
				string sPotentialError = "get event parameter <";
				sPotentialError += sParameter;
				sPotentialError = sPotentialError + "> failed!";
				FmodErrorOutput(sPotentialError.c_str(), eSLT_Message);
			}
			return nIndex;
		}

		if (pParameter)
		{
			m_ExResult = pParameter->getInfo(&nIndex, NULL);
			if (IS_FMODERROR)
			{
				if (bOutputWarning)
				{
					string sPotentialError = "get event parameter <";
					sPotentialError += sParameter;
					sPotentialError = sPotentialError + "> info failed!";
					FmodErrorOutput(sPotentialError.c_str(), eSLT_Message);
				}
			}

			m_ExResult = pParameter->getValue(fValue);
			if (IS_FMODERROR)
			{
				if (bOutputWarning)
				{
					string sPotentialError = "get event parameter <";
					sPotentialError += sParameter;
					sPotentialError = sPotentialError + "> value failed!";
					FmodErrorOutput(sPotentialError.c_str(), eSLT_Message);
				}
			}
		}
	}
	else
	{
		// queuing this value
		bool bNewEntry = CSound::m_pSSys->m_ParameterNames.AddOrSetParameter(sParameter, false, 0.0f, nIndex);

		if (bNewEntry)
		{
			*fValue = 0.0f;
			return nIndex;
		}

		if (bOutputWarning)
		{
			string sPotentialError = "get event parameter <";
			sPotentialError += sParameter;
			sPotentialError = sPotentialError + "> value failed!";
			FmodErrorOutput(sPotentialError.c_str(), eSLT_Message);
		}
	}

	return nIndex;
}

////////////////////////////////////////////////////////////////////////////
int CPlatformSoundFmodEx400Event::SetParamByName(const char *sParameter, float fValue, bool bOutputWarning)
{
	SQueuedParameter QParam;
	QParam.fValue = fValue;
	bool bFoundInName = false;
	
	assert( sParameter[0] );

	if (!sParameter[0])
		return QParam.nNameIndex;


	int nIndex = -1;
	bool bNewEntry = CSound::m_pSSys->m_ParameterNames.AddOrSetParameter(sParameter, false, 0.0f, nIndex);

	if (nIndex < (int)m_ParameterIndex.size())
	{
		QParam.nEventIndex = m_ParameterIndex[nIndex].nEventIndex;
		QParam.nNameIndex = nIndex;
		bFoundInName = true;
	}

	// Queue it if no event is there
	if (!m_pEvent || m_State == pssWAITINGTOFADEOUT)
	{
		bool bAlreadyFound = false;
		tvecQueuedParameterIter It2End = m_QueuedParameter.end();
		for (tvecQueuedParameterIter It2 = m_QueuedParameter.begin(); It2!=It2End; ++It2)
		{
			if ((*It2).nNameIndex == QParam.nNameIndex)
			{
				(*It2).fValue = fValue;
				bAlreadyFound = true;
				break;
			}
		}

		if (!bAlreadyFound && bFoundInName)
			m_QueuedParameter.push_back(QParam);

	}
	else
	{
		FMOD::EventParameter *pParameter = 0;

		{
			CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );

			// do not need to set the parameter if its pretty much the same value
			if (fabs(QParam.fValue - fValue) > 0.001f)
				return QParam.nNameIndex;
			
			if (QParam.nEventIndex != -1)
				m_ExResult = m_pEvent->getParameterByIndex(QParam.nEventIndex, &pParameter);
			else
			{
				// just set the error code
				m_ExResult = FMOD_ERR_EVENT_NOTFOUND;
			}
		}

		if (m_pSound->GetSoundSystemPtr()->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
			m_pAudioDevice->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENT_GETPARAMETER, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)m_pEvent), ptParamCHARP(sParameter), ptParamUINT32((uint32)pParameter));

		if (IS_FMODERROR)
		{
			if (bOutputWarning)
			{
				string sPotentialError = "set event parameter <";
				sPotentialError += sParameter;
				sPotentialError = sPotentialError + "> failed!";
				FmodErrorOutput(sPotentialError.c_str(), eSLT_Warning);
			}

			return QParam.nNameIndex;
		}

		if (pParameter)
		{

			if (m_pSound->GetSoundSystemPtr()->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
				m_pAudioDevice->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::PARAMETER_SETVALUE, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)pParameter), ptParamF32(fValue));

			{
				CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );
				
				if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_DETAIL)
				{
					float fMin, fMax;
					pParameter->getRange(&fMin, &fMax);
					CSound::m_pSSys->Log(eSLT_Always, "Event: %d Parameter %s value %1.2f out of Range %1.2f - %1.2f", m_pEvent, sParameter, fValue, fMin, fMax);
				}

				m_ExResult = pParameter->setValue(fValue);
			}

			if (IS_FMODERROR)
			{
				if (bOutputWarning)
				{
					string sPotentialError = "set event parameter <";
					sPotentialError += sParameter;
					sPotentialError = sPotentialError + "> value failed!";
					FmodErrorOutput(sPotentialError.c_str(), eSLT_Message);
				}
			}

			// all go and set
		}
	}

	return QParam.nNameIndex;;
}

////////////////////////////////////////////////////////////////////////////
int	CPlatformSoundFmodEx400Event::GetNameIndexByEventIndex(int const nEventIndex)
{
	for(size_t i=0; i<m_ParameterIndex.size(); ++i)
	{
		if(m_ParameterIndex[i].nEventIndex == nEventIndex)
		{
			return i;
		}
	}

	return -1;
}

void CPlatformSoundFmodEx400Event::InvalidateParameterCache()
{
	m_bParameterCacheValid = false;
}

void CPlatformSoundFmodEx400Event::ValidateParameterCache()
{
	m_bParameterCacheValid = false;
	m_fDistance = 0.0f;
	m_fVolume = 0.0f;
	m_bHasLoudness = false;
	m_fLoudness = 0.0f;
	
	if (m_pEvent)
	{
		if (m_nDistanceIndex != -1)
		{
			FMOD::EventParameter* pDistanceParameter = 0;
			m_ExResult = m_pEvent->getParameterByIndex(m_nDistanceIndex, &pDistanceParameter); 

			if (!(IS_FMODERROR))
				pDistanceParameter->getValue(&m_fDistance);
		}

		m_pEvent->getVolume(&m_fVolume);
		m_bHasLoudness = CPlatformSoundFmodEx400Event::GetProperty("loudness", &m_fLoudness);

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
		if(m_bHasLoudness)
		{
			//Loudness property should be of type float in fmod designer (when it's not the HDR tends to break...)
			int nPropertyIndex = -1;
			char* szLoudness = "loudness";
			FMOD_EVENTPROPERTY_TYPE ePropertyType = FMOD_EVENTPROPERTY_TYPE_INT;
			FMOD_RESULT ExResult = m_pEvent->getPropertyInfo(&nPropertyIndex, &szLoudness, &ePropertyType);

			assert(ExResult == FMOD_OK);
			assert(ePropertyType == FMOD_EVENTPROPERTY_TYPE_FLOAT);

			if(ePropertyType != FMOD_EVENTPROPERTY_TYPE_FLOAT)
			{
				string sError = "get property loudness failed (not of type float) on '";
				sError += m_sEventName;
				sError += "' - loudness property on this event should be changed to a float";
				FmodErrorOutput(sError.c_str(), eSLT_Error);
			}
		}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

		m_bParameterCacheValid = true;
	}
}

// Gets and Sets Parameter defined by index and float value
////////////////////////////////////////////////////////////////////////////
bool CPlatformSoundFmodEx400Event::GetParamByIndex(int nEventIndex,
																									 float* const pfValue,
																									 float* const pfRangeMin/* =NULL */,
																									 float* const pfRangeMax/* =NULL */,
																									 char const** const ppcParameterName/* =NULL */,
																									 bool const bOutputWarning/* =true */)
{
	CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );
	bool bSuccess = true;

	if (m_pEvent) 
	{
		// Make sure we don't access via a invalid index.
		int nNumParameters = 0;
		m_ExResult = m_pEvent->getNumParameters(&nNumParameters);

		if (IS_FMODOK)
		{
			if (nEventIndex >= 0 && nEventIndex < nNumParameters)
			{
				FMOD::EventParameter *pParameter = 0;
				m_ExResult = m_pEvent->getParameterByIndex(nEventIndex, &pParameter);

				if (IS_FMODOK)
				{
					m_ExResult = pParameter->getValue(pfValue);

					if (IS_FMODERROR)
					{
						bSuccess = false;

						if (bOutputWarning)
						{
							stack_string sPotentialError;
							sPotentialError = sPotentialError.Format("Get event parameter <index: %d> (getValue) failed!", nEventIndex);
							FmodErrorOutput(sPotentialError.c_str(), eSLT_Warning);
						}
					}

					if (ppcParameterName)
					{
						char* sName    = NULL;
						m_ExResult = pParameter->getInfo(NULL, &sName);

						if (IS_FMODOK)
						{
							*ppcParameterName = sName;
						}
						else
						{
							bSuccess = false;

							if (bOutputWarning)
							{
								stack_string sPotentialError;
								sPotentialError = sPotentialError.Format("Get event parameter <index: %d> (getInfo) failed!", nEventIndex);
								FmodErrorOutput(sPotentialError.c_str(), eSLT_Warning);
							}
						}
					}

					if (pfRangeMin)
					{
						m_ExResult = pParameter->getRange(pfRangeMin, NULL);

						if (IS_FMODERROR)
						{
							bSuccess = false;

							if (bOutputWarning)
							{
								stack_string sPotentialError;
								sPotentialError = sPotentialError.Format("Get event parameter <index: %d> (getRange pfRangeMin) failed!", nEventIndex);
								FmodErrorOutput(sPotentialError.c_str(), eSLT_Warning);
							}
						}
					}

					if (pfRangeMax)
					{
						m_ExResult = pParameter->getRange(NULL, pfRangeMax);

						if (IS_FMODERROR)
						{
							bSuccess = false;

							if (bOutputWarning)
							{
								stack_string sPotentialError;
								sPotentialError = sPotentialError.Format("Get event parameter <index: %d> (getRange pfRangeMax) failed!", nEventIndex);
								FmodErrorOutput(sPotentialError.c_str(), eSLT_Warning);
							}
						}
					}

					return bSuccess;
				}
				else
				{
					bSuccess = false;

					if (bOutputWarning)
					{
						stack_string sPotentialError;
						sPotentialError = sPotentialError.Format("Get event parameter <index: %d> (getParamByIndex) failed!", nEventIndex);
						FmodErrorOutput(sPotentialError.c_str(), eSLT_Warning);
					}
				}
			}
			else
			{
				bSuccess = false;

				if (bOutputWarning)
				{
					stack_string sPotentialError;
					sPotentialError = sPotentialError.Format("Get event parameter <index: %d> failed! (user passed invalid index)", nEventIndex);
					FmodErrorOutput(sPotentialError.c_str(), eSLT_Warning);
				}
			}
		}
		else
		{
			bSuccess = false;

			if (bOutputWarning)
			{
				stack_string sPotentialError;
				sPotentialError = sPotentialError.Format("Get event parameter <index: %d> (getNumParameters) failed!", nEventIndex);
				FmodErrorOutput(sPotentialError.c_str(), eSLT_Warning);
			}
		}
	}
	else
	{
		int nNameIndex = GetNameIndexByEventIndex(nEventIndex);

		if (nNameIndex != -1)
		{
			if(ppcParameterName)
				*ppcParameterName = CSound::m_pSSys->m_ParameterNames.GetNameByIndex(nNameIndex);

			if(pfValue)
				*pfValue = 0.0f;

			if(pfRangeMin)
				*pfRangeMin = m_ParameterIndex[nNameIndex].fRangeMin;

			if(pfRangeMax)
				*pfRangeMax = m_ParameterIndex[nNameIndex].fRangeMax;
		}
		else
		{
			bSuccess = false;

			if (bOutputWarning)
			{
				stack_string sPotentialError;
				sPotentialError = sPotentialError.Format("Get event parameter <index: %d> failed! (m_pEvent == NULL)", nEventIndex);
				FmodErrorOutput(sPotentialError.c_str(), eSLT_Warning);
			}
		}
	}

	return bSuccess;
}

////////////////////////////////////////////////////////////////////////////
bool CPlatformSoundFmodEx400Event::SetParamByIndex(int nEventIndex, float fValue, bool bOutputWarning)
{
	CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );

	// Make sure we don't access via a invalid index
	if (nEventIndex < 0)
		return false;

	if (!m_pEvent) 
	{
		SQueuedParameter QParam;
		QParam.nEventIndex	= nEventIndex;
		QParam.nNameIndex		= GetNameIndexByEventIndex(nEventIndex);
		QParam.fValue				= fValue;

		bool bAlreadyFound = false;
		tvecQueuedParameterIter It2End = m_QueuedParameter.end();
		for (tvecQueuedParameterIter It2 = m_QueuedParameter.begin(); It2!=It2End; ++It2)
		{
			if ((*It2).nNameIndex == QParam.nNameIndex)
			{
				(*It2).fValue = fValue;
				bAlreadyFound = true;
				break;
			}
		}

		if (!bAlreadyFound)
			m_QueuedParameter.push_back(QParam);

		return true;
	}

	if (m_pEvent) 
	{
		FMOD::EventParameter *pParameter = 0;
		m_ExResult = m_pEvent->getParameterByIndex(nEventIndex, &pParameter);

		if (IS_FMODERROR)
		{
			char cTemp[64] = {'\0'};
			sprintf_s(cTemp, 64, "%d", nEventIndex);

			string sPotentialError = "set event parameter <index: ";
			sPotentialError += cTemp;
			//sPotentialError += sParameter;
			sPotentialError += "> failed!";
			FmodErrorOutput(sPotentialError.c_str(), eSLT_Warning);
			return false;
		}

		if (pParameter)
		{
			if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_DETAIL)
			{
				float fMin, fMax;
				pParameter->getRange(&fMin, &fMax);
				char* sParameterName = 0;
				pParameter->getInfo(0, &sParameterName);
				CSound::m_pSSys->Log(eSLT_Always, "Event %d Parameter %s value %1.2f out of Range %1.2f - %1.2f", m_pEvent, sParameterName, fValue, fMin, fMax);
			}

			m_ExResult = pParameter->setValue(fValue);
			if (IS_FMODERROR)
			{
				if (bOutputWarning)
				{
					char cTemp[64] = {'\0'};
					sprintf_s(cTemp, 64, "%d", nEventIndex);

					string sPotentialError = "set event parameter <index: ";
					sPotentialError += cTemp;
					//sPotentialError += sParameter;
					sPotentialError += "> value failed!";
					FmodErrorOutput(sPotentialError.c_str(), eSLT_Message);

					float fVerify = 0;
					pParameter->getValue(&fVerify);

					float fMin, fMax;
					pParameter->getRange(&fMin, &fMax);

					if (fValue < fMin || fValue > fMax)
					{
						char* sParameterName = 0;
						pParameter->getInfo(0, &sParameterName);
						CryLogAlways("Parameter %s value %1.2f out of Range %1.2f - %1.2f", sParameterName, fValue, fMin, fMax);
					}

					if ( abs(fVerify-fValue) > 0.01 && m_State != pssINFOONLY)
					{
						char* sParameterName = 0;
						pParameter->getInfo(0, &sParameterName);
						CryLogAlways("Parameter %s set to %1.2f, got %1.2f back", sParameterName, fValue, fVerify);
					}
				}
				return false;
			}
		}
		return true;
	}
	return false;
}

void CPlatformSoundFmodEx400Event::SetMultipleParams(const SParameter* params, size_t numParams, bool outputWarning)
{
	const SParameter* const paramsEnd = params + numParams;
	for (const SParameter* p = params; p != paramsEnd; ++ p)
	{
		if (p->nNameIndex >= m_ParameterIndex.size())
		{
			UpdateParameter(p->nNameIndex);
		}

		CPlatformSoundFmodEx400Event::SetParamByIndex(m_ParameterIndex[p->nNameIndex].nEventIndex, p->fValue, outputWarning);
	}
}

////////////////////////////////////////////////////////////////////////////
bool CPlatformSoundFmodEx400Event::IsInCategory(const char* sCategory)
{
	if (!m_pEvent)
		return false;

	FMOD::EventCategory *pCat = NULL;

	{
		CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );
		m_ExResult = m_pEvent->getCategory(&pCat);
	}

	if (m_ExResult == FMOD_OK && pCat)
	{
		int nIndex = 0;
		char *sName = 0; 

		{
			CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );
			pCat->getInfo( &nIndex, &sName );
		}

		if (strcmp(sCategory, sName) == 0)
			return true;
	}

	return false;
}

////////////////////////////////////////////////////////////////////////////
void  CPlatformSoundFmodEx400Event::GetMemoryUsage(ICrySizer* pSizer) const
{
	pSizer->AddObject(m_QueuedParameter);
	pSizer->AddObject(m_ParameterIndex);

	size_t nSize = sizeof(*this);

	if (m_pEvent)
	{
		int nIndex;
		char *name; 
		//char **soundbanks;
		TFixedResourceName sBankName;
		FMOD_EVENT_INFO EventInfo;

		// first get number of wavebanks
		m_ExResult = m_pEvent->getInfo(&nIndex, &name, &EventInfo);

		if (IS_FMODERROR)
		{
			const_cast<CPlatformSoundFmodEx400Event*>(this)->FmodErrorOutput("event system get event info failed! ", eSLT_Warning);
			return;
		}

		FMOD_EVENT_WAVEBANKINFO* pWavebankInfo = NULL;

		if (EventInfo.maxwavebanks)
		{
			// now repeat to fill the array of wavebank infos
			pWavebankInfo = new FMOD_EVENT_WAVEBANKINFO[EventInfo.maxwavebanks];

			if (!pWavebankInfo)
				return;


			EventInfo.wavebankinfo = pWavebankInfo;

			m_ExResult = m_pEvent->getInfo(0, 0, &EventInfo);
			if (IS_FMODERROR)
			{
				const_cast<CPlatformSoundFmodEx400Event*>(this)->FmodErrorOutput("event system get event info with wavebanks failed! ", eSLT_Warning);
				delete[] pWavebankInfo; // remove wavebankinfo array
				return;
			}
		}

		for (int i=0; i<EventInfo.maxwavebanks; ++i)
		{
			sBankName = pWavebankInfo[i].name;

			if (m_pSound->GetSoundBufferPtr())
			{
				// Compose full File name to Wavebank
				TFixedResourceName sFilePath = m_pSound->GetSoundBufferPtr()->GetProps()->sName;
				const char *pInput = sFilePath;
				const char *pColon = strstr(pInput, ":");

				if (pInput && pColon)
				{
					sFilePath.assign(sFilePath.substr(0, pColon - pInput));
					sFilePath += "/";

					//sBankName.assign(pWavebankInfo[nBankIndex].name, strlen(pWavebankInfo[nBankIndex].name));
					sBankName += ".fsb";

					sBankName = sFilePath + sBankName;
				}
			}

			IResourceCollector* pColl = pSizer->GetResourceCollector();
			if (pColl)
			{
				pColl->AddResource(sBankName.c_str(), 0xffffffff);
			}
		}
		
		delete[] pWavebankInfo; // remove wavebankinfo array

	}
	//nSize += m_pSoundAssetManager->GetMemUsage();
	pSizer->AddObject(this, nSize);

}

//////////////////////////////////////////////////////////////////////////
bool CPlatformSoundFmodEx400Event::StartSound(bool bStartPaused)
{

	//m_ExResult = m_pEvent->set3DOcclusion(1.0f, 0.0f);

	if (m_pEvent && m_State != pssINFOONLY)
	{
		//m_ExResult = m_pEvent->setVolume(0.0f);

		if (m_pSound->GetSoundSystemPtr()->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
			m_pAudioDevice->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENT_PLAY, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)m_pEvent));

		if (m_State != pssPAUSED && m_pSound->m_pSSys->g_nProfiling > 1)
		{
			float f0 = gEnv->pTimer->GetAsyncCurTime();
			{
				FRAME_PROFILER( "SoundSystem::FMOD-StartEvent", GetISystem(), PROFILE_SOUND );	
				m_ExResult = m_pEvent->start();
			}

			float f1 = gEnv->pTimer->GetAsyncCurTime();
			CryLog(" <Sound Profile starting> %s %.2f", m_pSound->GetName(), (f1-f0)*1000.0f);
		}
		else
		{
			FRAME_PROFILER( "SoundSystem::FMOD-StartEvent", GetISystem(), PROFILE_SOUND );	
			//SetParam("distance", 0.0f);
			m_ExResult = m_pEvent->start();
		}

		if (IS_FMODERROR || !m_pEvent)
		{
			// Callbacks set m_pEvent to zero or something else went wrong
			m_ExResult = FMOD_ERR_EVENT_FAILED; // set error so output works
			FmodErrorOutput("start event failed! ", eSLT_Warning);
			return false;
		}

		FMOD_EVENT_STATE EventState;
		{
			CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_START_SOUND) );
			m_ExResult = m_pEvent->getState(&EventState);
		}

		if (IS_FMODERROR)
		{
			FmodErrorOutput("get event state failed! ", eSLT_Warning);
			return false;
		}

		if (EventState & FMOD_EVENT_STATE_PLAYING)
			m_State = pssPLAYING;

		if (EventState & FMOD_EVENT_STATE_LOADING)
			m_State = pssLOADING;

		//if (bStartPaused)
		//{
		//	m_ExResult = m_pEvent->setPaused(true);
		//	m_State = pssPAUSED;
		//}
		//else
		//{
		//	m_ExResult = m_pEvent->setPaused(false);
		//	m_State = pssPLAYING;
		//}

			return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CPlatformSoundFmodEx400Event::StopSound(const bool bEnforce)
{
	bool bSuccess = true;

	if (m_pEvent && m_State != pssINFOONLY && m_State != pssSTOPPED)
	{
		if ((m_State != pssENDING) || bEnforce) // prevent loop of stopping while ending
		{
			if (m_pSound->GetSoundSystemPtr()->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
				m_pAudioDevice->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENT_STOP, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)m_pEvent));
			
			bool const bAtOnce = (m_pSound->m_pSSys->g_nStopSoundsImmediately!=0) || (m_pSound->m_eStopMode == ESoundStopMode_AtOnce);

			if (bAtOnce || m_pSound->m_eStopMode != ESoundStopMode_OnSyncPoint)
			{
				if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_PROGRAMMERSOUND && m_pSound->GetFlags() & FLAG_SOUND_VOICE)
					CSound::m_pSSys->Log(eSLT_Always, "----> Event %s Before STOP", m_sEventName.c_str());

				CFrameProfilerSection const SoundFrameProfilerSection(CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_STOP_SOUND));

				FMOD_EVENT_STATE nEventState;
				m_ExResult = m_pEvent->getState(&nEventState);

				if ((nEventState & FMOD_EVENT_STATE_LOADING) == 0)
				{
					// We assume this event to be ending first if however bAtOnce is set to true the event stop call will set m_State to pssSTOPPED via the callback.
					m_State = pssENDING;

					// Only now it's safe to stop this event.
					// Stop on an event that's still loading will put it into FMOD_EVENT_STATE_SEEKING which causes a freeze within FMOD during project release at the moment.
					m_ExResult = m_pEvent->stop(bAtOnce);

					if (IS_FMODERROR)
					{
						FmodErrorOutput("stop event failed! ", eSLT_Warning);
						m_State = pssNONE;
						m_pEvent = NULL;
					}
				}
				else
				{
					// The event is still loading, this will make it come back again.
					bSuccess = false;
				}

				if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_PROGRAMMERSOUND && m_pSound->GetFlags() & FLAG_SOUND_VOICE)
					CSound::m_pSSys->Log(eSLT_Always, "----> Event %s After STOP", m_sEventName.c_str());
			}
			else
			{
				m_State = pssWAITINGFORSYNC;

				if (CSound::m_pSSys->g_nDebugSound == SOUNDSYSTEM_DEBUG_SYNCPOINTS)
					CSound::m_pSSys->Log(eSLT_Message, "<Sound> --> Waiting for Syncpoint: %s, Time:%.2f", m_pSound->GetName(), gEnv->pTimer->GetAsyncCurTime());
			}
		}
	}

	return bSuccess;
}

//////////////////////////////////////////////////////////////////////////
bool CPlatformSoundFmodEx400Event::FreeSoundHandle()
{
	if (m_pEvent)
	{
		CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_EVENT_OTHER) );
			
		StopSound();	
		
		// deactivate callback for now
		if (m_State != pssINFOONLY)
		{
			FMOD_EVENT_STATE State;
			m_ExResult = m_pEvent->getState(&State);

			if (!(State & FMOD_EVENT_STATE_CHANNELSACTIVE))
			{
				m_ExResult = m_pEvent->setCallback(NULL, INVALID_SOUNDID); //OnCallBack
				m_pEvent = NULL;
				m_State = pssNONE;
			}
			else
			{
				m_pEvent = NULL;
				m_State = pssWAITINGTOFADEOUT;
			}
		}
		else
		{
			m_pEvent = NULL;
			m_State = pssNONE;
		}

		// TODO Generic approach to Playmodes (Play/Pause/Stop)
		// Stopping a Stream does not stop the streaming itself !

		//To actually free a channel FreeSoundHandle has to be called.
		// if it was a stream, or if it was the only ref to the Asset, the Asset will stop.
	}

	return true;
}


//////////////////////////////////////////////////////////////////////////
enumPlatformSoundErrorCodes const CPlatformSoundFmodEx400Event::CreateSound(tAssetHandle const AssetHandle, SSoundSettings const& SoundSettings)
{
	// TODO Verify that we dont need any StopChannel or (m_nChannel != CS_FREE) checks at all
	CSoundBuffer* SoundBuf	= NULL;
	SoundBuf								= m_pSound->GetSoundBufferPtr();

	ptParamF32		fParam(0.0f);
	ptParamINT32	nParam(0);

	if (!SoundBuf)
		return pssBUFFER_INVALID;

	UpdateFMODState();

	if (!AssetHandle)
		return pssBUFFER_HANDLE_INVALID;

	if (!SoundBuf || SoundBuf->LoadFailure())
		return pssBUFFER_INVALID;

	bool bSoundBufLoaded = (SoundBuf->Loaded(true) || m_pSound->m_pEventGroupBuffer);

	if (!bSoundBufLoaded)
		return pssBUFFER_NOT_AVAILIBLE;

	switch (SoundBuf->GetProps()->eBufferType)
	{
	case btNETWORK:
		{

			return pssBUFFER_BAD_TYPE;
			break;
		}
	case btEVENT: case btSAMPLE:
		{
			if (m_pEvent && m_State==pssINFOONLY && !SoundSettings.bLoadData)
				return pssOK;

			if (m_pEvent && m_State!=pssINFOONLY && SoundSettings.bLoadData)
				return pssOK;

			m_pEvent = NULL;
			m_State = pssNONE;
			FMOD::EventGroup  *pEventGroup = NULL;

			if (SoundBuf->GetProps()->eBufferType == btSAMPLE)
				pEventGroup = (FMOD::EventGroup*)m_pSound->m_pEventGroupBuffer->GetAssetHandle();
			else
				pEventGroup = (FMOD::EventGroup*)AssetHandle;

			if (!pEventGroup)
			{
				return pssBUFFER_HANDLE_INVALID;
			}

			// first get the INFOONLY event retrieve info
			if (!m_pEvent && m_State != pssINFOONLY && !SoundSettings.bLoadData)
			{
				FMOD_EVENT_MODE EventMode = FMOD_EVENT_INFOONLY;
				{
					CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_CREATE_SOUND) );
					m_ExResult = pEventGroup->getEvent((char*)m_sEventName.c_str(), EventMode, &m_pEvent);
				}

				if (m_pSound->GetSoundSystemPtr()->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
					m_pAudioDevice->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::GROUP_GETEVENT, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)pEventGroup), ptParamCHARP(m_sEventName.c_str()), ptParamUINT32((uint32)EventMode), ptParamUINT32((uint32)m_pEvent));

				if (IS_FMODERROR)
				{
					FmodErrorOutput("get info event failed! (Event does not exist!) ", eSLT_Message);
					m_State = pssERROR;
					return pssEVENT_UNKNOWN;
				}

				m_State = pssINFOONLY;

				// Update the event parameters
				UpdateParameters();

				fParam.SetValue(m_fMaxRadius);
				GetParamByType(pspMAXRADIUS, &fParam);
				fParam.GetValue(m_fMaxRadius);

				float fTemp = 0.0f;
				GetParamByType(pspVOLUME, &fParam);
				fParam.GetValue(fTemp);
				if (m_fMaxVolume < 0)
					m_fMaxVolume = fTemp;
				assert(m_fMaxVolume != 0.0f);

				FMOD_EVENT_INFO Info;

				m_ExResult = m_pEvent->getInfo(&m_nEventIndex, 0, &Info);
				if (IS_FMODERROR)
				{
					FmodErrorOutput("get event info <length ms> failed! ", eSLT_Message);
					return pssEVENT_INVALID;
				}

				// TODO: Clean this code block up and find a faster way of determining FMOD preload status
				if (Info.maxwavebanks)
				{
					// if we have more than 3 wavebanks for this event, make it not AFCM cached for now
					if (Info.maxwavebanks < 4)
					{
						FMOD_EVENT_WAVEBANKINFO aWaveBankInfo[3];
						Info.wavebankinfo = &aWaveBankInfo[0];

						m_ExResult = m_pEvent->getInfo(&m_nEventIndex, 0, &Info);

						FMOD::EventProject* pEventProject;
						pEventGroup->getParentProject(&pEventProject);

						FMOD_EVENT_PROJECTINFO projectInfo;
						pEventProject->getInfo(&projectInfo);

						int i = 0;
						for (; i < Info.maxwavebanks; ++i)
						{
							FMOD_EVENT_WAVEBANKINFO& waveBankInfo = Info.wavebankinfo[i];

							if (waveBankInfo.type != 0)
							{
								CryFixedStringT<AFCM_FILE_NAME_LENGTH> sName;
								sName.Format("sounds/%s/%s.fsb", projectInfo.name, waveBankInfo.name);

								EAudioFileCacheState eAudioFileCacheState;
								bool const bFileFound = m_pSound->GetSoundSystemPtr()->GetFileCacheManager()->GetFileCacheEntryState(sName.c_str(), eAudioFileCacheState);

								// Break out of the loop as soon as we can't find a sound bank.
								if (!bFileFound)
								{
									m_bAFCMCached      = false;
									m_bPreloadedToFMOD = false;

									break;
								}

								// We do not play events that have sound banks being queued unload.
								if ((eAudioFileCacheState & (eAFCS_QUEUED_FOR_UNLOAD)) != 0)
								{
									FmodErrorOutput("The sound bank is queued for unload!", eSLT_Message);

									m_bAFCMCached      = false;
									m_bPreloadedToFMOD = false;

									return pssEVENT_INVALID;
								}

								size_t nCounter = 0;

								// We wait here until the data is delivered that usually takes just one update cycle.
								CCryNameCRC const oNameCRC(sName);

								while ((eAudioFileCacheState & (eAFCS_LOADING|eAFCS_QUEUED_FOR_PRELOAD)) != 0)
								{
									// Let any pending FMOD patching take place then try again
									FmodErrorOutput("Waiting for the soundbank to finish loading/preloading.", eSLT_Message);
									gEnv->pSystem->GetStreamEngine()->Update(STREAM_TASK_TYPE_AUDIO_ALL);
									m_pSound->GetSoundSystemPtr()->GetFileCacheManager()->Update();
									m_pSound->GetSoundSystemPtr()->GetFileCacheManager()->GetFileCacheEntryState(oNameCRC, eAudioFileCacheState);

									if (++nCounter > 99)
									{
										// Fail save to prevent getting stuck in this loop!
										m_bAFCMCached      = false;
										m_bPreloadedToFMOD = false;

										return pssEVENT_INVALID;
									}
								}

								// As the PS3 naturally doesn't preload this must be always false on that platform.
								if ((eAudioFileCacheState & eAFCS_PRELOADED) == 0)
								{
									// One of the sound banks is obviously not preloaded.
									m_bPreloadedToFMOD = false;
								}

								if ((eAudioFileCacheState & eAFCS_CACHED) == 0)
								{
									// One of the sound banks is obviously not even AFCM cached.
									m_bAFCMCached = false;
								}
							}
							else
							{
								// Break out of the loop if the event gets its data from a streaming sound bank.
								m_bAFCMCached      = false;
								m_bPreloadedToFMOD = false;

								break;
							}
						}
					}
					else
					{
						// This event refers to more than 3 sound banks.
						// TODO: Handle this more appropriately!
						m_bAFCMCached      = false;
						m_bPreloadedToFMOD = false;
					}
				}
				else
				{
					// This could be a voice line and therefore valid.
					m_bAFCMCached      = false;
					m_bPreloadedToFMOD = false;
				}

//				m_nCurrentSpawns = Info.instancesactive;
				m_nLenghtInMs = Info.lengthms;

				m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_MAX_PLAYBACKS, &m_nMaxSpawns);
				if (IS_FMODERROR)
				{
					FmodErrorOutput("get event property <max playbacks> failed! ", eSLT_Message);
					return pssEVENT_INVALID;
				}

				// PS3 Temp optimization
				///m_nMaxSpawns = m_nMaxSpawns/1.5f;
				//m_ExResult = m_pEvent->setPropertyByIndex(FMOD_EVENTPROPERTY_MAX_PLAYBACKS, &m_nMaxSpawns, false);
				// PS3 Temp optimization




				//EVENTPROPERTY_MAX_PLAYBACKS
				FMOD_MODE Mode;
				m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_MODE, &Mode);
				if (IS_FMODERROR)
				{
					FmodErrorOutput("get Property Mode failed!", eSLT_Warning);
					return pssPROPERTY_INVALID;
				}

				int nPriority = 0;
				m_ExResult = m_pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_PRIORITY, &nPriority);
				if (IS_FMODERROR)
				{
					FmodErrorOutput("get Property Priority failed!", eSLT_Warning);
					return pssPROPERTY_INVALID;
				}
			}	

			// now set all parameters to the info only event
			if (m_pEvent && m_State == pssINFOONLY)
			{
				// Reset TimeOffset on infoonly event
				//float fMS = 0.0f;
				//ptParamF32 fParam(fMS);
				//SetParamByType(pspTIMEPOSITION, &fParam);

				// set queued Parameters
				int nNum = m_QueuedParameter.size();
				for (int i=0; i<nNum; ++i)
				{
					if (m_QueuedParameter[i].nEventIndex != -1)
					{
						SetParamByIndex(m_QueuedParameter[i].nEventIndex, m_QueuedParameter[i].fValue, false);
						continue;
					}

					if (m_QueuedParameter[i].eSemantic != pspNONE)
					{
						nParam.SetValue(m_QueuedParameter[i].nValue);
						fParam.SetValue(m_QueuedParameter[i].fValue);
						SetParamByType(m_QueuedParameter[i].eSemantic, &nParam);
						SetParamByType(m_QueuedParameter[i].eSemantic, &fParam);
						continue;
					}

					// should never go here
					if (m_QueuedParameter[i].nEventIndex > -1)
					{
						SetParamByIndex(m_QueuedParameter[i].nEventIndex, m_QueuedParameter[i].fValue, false);
						continue;
					}
				}
			}

			// now spawn the real event if needed
			if (SoundSettings.bLoadData)
			{
				m_pEvent = NULL;
				m_State  = pssNONE;

				CSoundSystem* pSys = m_pSound->GetSoundSystemPtr();

				if (pSys)
				{
					if (pSys->g_nProfiling < 2)
					{
						CreateFModEvent(pEventGroup);
					}
					else
					{
						float const f0 = gEnv->pTimer->GetAsyncCurTime();
						CreateFModEvent(pEventGroup);
						float const f1 = gEnv->pTimer->GetAsyncCurTime();

						CryLog("<Sound Profile Getting Event> %s %.2f", m_sEventName.c_str(), (f1-f0)*1000.0f);
					}
				}

				if (IS_FMODERROR)
				{
					if (m_ExResult == FMOD_ERR_EVENT_MISMATCH)
					{
						m_pEvent = NULL;
						m_State = pssERROR;
						m_pSound->Update();
						return pssEVENT_NOT_AVAILIBLE;
					}

					if (m_ExResult == FMOD_ERR_EVENT_FAILED)
					{
						m_State = pssNONE;
						return pssEVENT_NOT_AVAILIBLE;
					}

					FmodErrorOutput("create event failed! (Typo in event name, event not available or low memory?)", eSLT_Error);
					return pssEVENT_UNKNOWN;
				}

				// set queued Parameters again TODO check if that is needed
				for (uint32 i=0; i<m_QueuedParameter.size(); ++i)
				{
					if (m_QueuedParameter[i].nEventIndex != -1)
					{
						SetParamByIndex(m_QueuedParameter[i].nEventIndex, m_QueuedParameter[i].fValue, false);
						continue;
					}

					if (m_QueuedParameter[i].eSemantic != pspNONE)
					{
						nParam.SetValue(m_QueuedParameter[i].nValue);
						fParam.SetValue(m_QueuedParameter[i].fValue);
						SetParamByType(m_QueuedParameter[i].eSemantic, &nParam);
						SetParamByType(m_QueuedParameter[i].eSemantic, &fParam);
						continue;
					}

					// should never go here
					if (m_QueuedParameter[i].nEventIndex > -1)
					{
						SetParamByIndex(m_QueuedParameter[i].nEventIndex, m_QueuedParameter[i].fValue, false);
						continue;
					}
				}
				////

				UpdateFMODState();

				if (m_State != pssINFOONLY)
				{
					// activate callback for now
					m_ExResult = m_pEvent->setCallback(OnCallBack, (void*)m_pSound->GetId());
				}

				if (IS_FMODERROR)
				{
					FmodErrorOutput("set callback on event failed!", eSLT_Warning);
				}

				break;
			}
		}
	}

	if (m_pSound->GetSoundSystemPtr()->g_nProfiling > 0 && m_pEvent)
	{
		FMOD_EVENT_INFO EventInfo;

		// first get number of wavebanks
		m_ExResult = m_pEvent->getInfo(0, 0, &EventInfo);

		if (IS_FMODERROR)
		{
			FmodErrorOutput("event system get event info failed! ", eSLT_Warning);
			return pssEVENT_INVALID;
		}

		FMOD_EVENT_WAVEBANKINFO* pWavebankInfo = NULL;

		if (EventInfo.maxwavebanks)
		{
			// now repeat to fill the array of wavebank infos
			pWavebankInfo = new FMOD_EVENT_WAVEBANKINFO[EventInfo.maxwavebanks];

			if (!pWavebankInfo)
				return pssMEMORY_ERROR;

			EventInfo.wavebankinfo = pWavebankInfo;

			m_ExResult = m_pEvent->getInfo(0, 0, &EventInfo);
			if (IS_FMODERROR)
			{
				FmodErrorOutput("event system get event info with wavebanks failed! ", eSLT_Warning);
				delete[] pWavebankInfo; // remove wavebankinfo array
				return pssEVENT_INVALID;
			}
		}


		for (int i=0; i<EventInfo.maxwavebanks; ++i)
		{
			IWavebank *pWavebank = m_pSound->GetSoundSystemPtr()->GetIAudioDevice()->GetWavebank(pWavebankInfo[i].name);
		
			if (pWavebank && strcmp(pWavebank->GetPath(),"") == 0)
			{
				if (m_pSound->GetSoundBufferPtr())
				{
					// Compose full File name to Wavebank
					TFixedResourceName sFilePath = m_pSound->GetSoundBufferPtr()->GetProps()->sName;
					const char *pInput = sFilePath;
					const char *pColon = strstr(pInput, ":");

					if (pInput && pColon)
					{
						sFilePath.assign(sFilePath.substr(0, pColon - pInput));
						sFilePath += "/";
						pWavebank->SetPath(sFilePath.c_str());
					}
				}
			}
		}

		delete[] pWavebankInfo; // remove wavebankinfo array
	}

	ValidateParameterCache();

	return pssOK;
}

////////////////////////////////////////////////////////////////////////////
void CPlatformSoundFmodEx400Event::UpdateFMODState()
{
	m_State = pssNONE;

	if (m_pEvent)
	{
		FMOD_EVENT_STATE EventState;
		m_ExResult = m_pEvent->getState(&EventState);

		if (IS_FMODERROR)
		{
			FmodErrorOutput("get event state failed! ", eSLT_Warning);

			return;
		}

		m_State = pssINFOONLY;

		if ((EventState & FMOD_EVENT_STATE_INFOONLY) == 0)
		{
			if ((EventState & FMOD_EVENT_STATE_READY) != 0)
			{
				m_State = pssREADY;
			}

			if ((EventState & FMOD_EVENT_STATE_PLAYING) != 0)
			{
				m_State = pssPLAYING;
			}

			if ((EventState & FMOD_EVENT_STATE_LOADING) != 0)
			{
				m_State = pssLOADING;
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////
bool CPlatformSoundFmodEx400Event::GetProperty(const char *pPropertyname, void *pValue)
{
	return (FMOD_OK == m_pEvent->getProperty(pPropertyname, pValue));
}

bool CPlatformSoundFmodEx400Event::GetLoudnessProperty(float& pValue)
{
	if (m_bHasLoudness)
	{
		pValue = m_fLoudness;
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CPlatformSoundFmodEx400Event::UpdateStreamCount(bool const bIncrement)
{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE	
	if (m_pEvent)
	{
		// Don't update this in Release builds as this is currently more of a debugging information
		FMOD_EVENT_INFO Info;
		FMOD_EVENT_WAVEBANKINFO BankInfo = {{0},0};
		Info.maxwavebanks = 1;
		Info.wavebankinfo = &BankInfo;

		m_ExResult = m_pEvent->getInfo(0, 0, &Info);
		if (IS_FMODERROR)
		{
			assert(0);
			return;
		}

		// This test only evaluates the first wavebank
		// Type 0 indicates a streamed wavebank
		if (BankInfo.type == 0)
			bIncrement ? ++CSoundSystem::g_nOverallStreamsCount : --CSoundSystem::g_nOverallStreamsCount;
	}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE
}

//////////////////////////////////////////////////////////////////////////
void CPlatformSoundFmodEx400Event::CreateFModEvent(FMOD::EventGroup* const pEventGroup)
{
	FMOD_EVENT_MODE EventMode = FMOD_EVENT_DEFAULT;

	if (m_bPreloadedToFMOD)
	{
		// Events coming from FMOD preloaded sound banks must be loaded synchronously!
		CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_CREATE_SOUND) );
		m_ExResult = pEventGroup->getEventByIndex(m_nEventIndex, EventMode, &m_pEvent);
	}
	else
	{
		EventMode             = FMOD_EVENT_NONBLOCKING_THREAD0;
		size_t nThreadIDToUse = 0;

		// g_SoundCVars.g_nNumLoadingThreadsToUse is clamped between 1 and 5
		if (g_SoundCVars.g_nNumLoadingThreadsToUse == 2)
		{
			EventMode      = m_bAFCMCached ? FMOD_EVENT_NONBLOCKING_THREAD0 : FMOD_EVENT_NONBLOCKING_THREAD1;
			nThreadIDToUse = m_bAFCMCached ? 0 : 1;
		}
		else if (g_SoundCVars.g_nNumLoadingThreadsToUse > 2)
		{
			if (!m_bAFCMCached)
			{
				switch (m_pAudioDevice->GetLeastUsedLoadingThreadID())
				{
				case 0:
				case 1:
					{
						// Thread ID 0 is reserved for AFCM cached entries.
						// Just forward this request to thread ID 1 regardless of its business.
						EventMode      = FMOD_EVENT_NONBLOCKING_THREAD1;
						nThreadIDToUse = 1;

						break;
					}
				case 2:
					{
						EventMode      = FMOD_EVENT_NONBLOCKING_THREAD2;
						nThreadIDToUse = 2;

						break;
					}
				case 3:
					{
						EventMode      = FMOD_EVENT_NONBLOCKING_THREAD3;
						nThreadIDToUse = 3;

						break;
					}
				case 4:
					{
						EventMode      = FMOD_EVENT_NONBLOCKING_THREAD4;
						nThreadIDToUse = 4;

						break;
					}
				}
			}
		}

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
		size_t const nLoadRequestsBefore = m_pAudioDevice->GetNumLoadRequests(nThreadIDToUse);
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

		CFrameProfilerSection SoundFrameProfilerSection(CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_CREATE_SOUND));
		m_ExResult = pEventGroup->getEventByIndex(m_nEventIndex, EventMode, &m_pEvent);

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
		size_t const nLoadRequestsAfter = m_pAudioDevice->GetNumLoadRequests(nThreadIDToUse);

		if (!IS_FMODERROR && nLoadRequestsAfter > nLoadRequestsBefore)
		{
			// If not preloaded to FMOD this request will end up in FMOD's load-queue and might get delayed.
			CSoundSystem* const pSoundSystem = static_cast<CSoundSystem* const>(gEnv->pSoundSystem);

			if (pSoundSystem)
			{
				pSoundSystem->AddLoadRequestInfo(nThreadIDToUse, m_pSound->GetSoundBufferPtr()->GetProps()->sName.c_str(), m_sEventName.c_str());
			}
		}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE
	}

	if (m_pSound->GetSoundSystemPtr()->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
	{
		m_pAudioDevice->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::GROUP_GETEVENT, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)pEventGroup), ptParamCHARP(m_sEventName.c_str()), ptParamUINT32((uint32)EventMode), ptParamUINT32((uint32)m_pEvent));
	}
}

//////////////////////////////////////////////////////////////////////////
void CPlatformSoundFmodEx400Event::UpdateParameters()
{
	// Retrieve all available parameters and store their name indices
	int nNumParameters = 0;
	m_ExResult         = m_pEvent->getNumParameters(&nNumParameters);
	assert(IS_FMODOK);

	for (int nIndex = 0; nIndex < nNumParameters; ++nIndex)
	{
		UpdateParameter(nIndex);
	}
}

//////////////////////////////////////////////////////////////////////////
void CPlatformSoundFmodEx400Event::UpdateParameter(int const nIndex)
{
	char* sName                      = NULL; 
	FMOD::EventParameter* pParameter = NULL;

	{
		CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_CREATE_SOUND) );
		m_ExResult = m_pEvent->getParameterByIndex(nIndex, &pParameter);
	}

	if (IS_FMODOK)
	{
		m_ExResult = pParameter->getInfo(NULL, &sName);

		if (IS_FMODOK)
		{
			int nNameIndex = -1;
			bool bNewEntry = CSound::m_pSSys->m_ParameterNames.AddOrSetParameter(sName, false, 0.0f, nNameIndex);

			// Make sure that m_ParameterIndex matches the size of m_ParameterNames
			assert(nNameIndex < (int)m_ParameterIndex.size());

			if (nNameIndex >= (int)m_ParameterIndex.size())
			{
				m_ParameterIndex.resize(CSound::m_pSSys->m_ParameterNames.size());
			}

			// Fill event parameter struct
			float fRangeMin = 0.0f;
			float fRangeMax = 0.0f;
			m_ExResult      = pParameter->getRange(&fRangeMin, &fRangeMax);
			assert(IS_FMODOK);

			m_ParameterIndex[nNameIndex].nEventIndex	= nIndex;
			m_ParameterIndex[nNameIndex].fRangeMin		= fRangeMin;
			m_ParameterIndex[nNameIndex].fRangeMax		= fRangeMax;

			// shortcut for distance parameter
			if (strcmp(sName, "distance") == 0)
			{
				m_nDistanceIndex      = nIndex;
				m_nDistanceParamIndex = nNameIndex;
			}
		}
		else
		{
			FmodErrorOutput("get parameter info failed!", eSLT_Message);
		}
	}
	else
	{
		// In case the passed index is an invalid parameter make still sure that m_ParameterIndex matches the size of m_ParameterNames
		assert(nIndex < (int)m_ParameterIndex.size());

		if (nIndex >= (int)m_ParameterIndex.size())
		{
			m_ParameterIndex.resize(CSound::m_pSSys->m_ParameterNames.size());
		}
	}
}

#endif
